using System;
using System.Diagnostics;

namespace OpenRay
{
	//Immutable Class
	public abstract class PhongLight : Light
	{
		private readonly Color ambient;
		private readonly Color diffuse;
		private readonly Color specular;
		private readonly IScene scene;
		
		protected PhongLight( Color A, Color D, Color S, IScene s ) {
			ambient = A;
			diffuse = D;
			specular = S;
			scene = s;
		}
		
		public abstract Color ComputeLighting( Color materialAmbient, Color materialDiffuse, Color materialSpecular, float materialShininess,
                                 	   		   Point worldHit, Vector normalizedViewer, Vector surfaceNormal );
		
		protected bool isShadowed( Point worldHit, Vector ToLight )
		{
			return ( scene != null && scene.Trace(new Ray(worldHit, ToLight), 1).Count > 0 );
		}
		
		//Calculate Ambient Term
		protected Color BlinnPhongAmbient( Color materialAmbient )
		{			
			return materialAmbient * ambient;	
		}
		
		//Implements Blinn-Phong shading model
		protected Color BlinnPhongNonAmbient( Color materialDiffuse, Color materialSpecular, float materialShininess,
		                                      Vector normalizedToLight, Vector normalizedViewer, Vector surfaceNormal )
		{
			Debug.Assert( normalizedViewer.IsNormal() );
			Debug.Assert( normalizedToLight.IsNormal() );
			
			//Calculate Diffuse Term
			float diffuseCoeff = Math.Max( 0.0f, Vector.DotProduct( surfaceNormal, normalizedToLight) );
			Color ret = diffuseCoeff * materialDiffuse * diffuse;
			
			//Calculate Specular Term
			float specularCoeff = (float)Math.Pow( Math.Max( 0.0f, Vector.DotProduct( surfaceNormal, Vector.HalfAngle(normalizedViewer, normalizedToLight) ) ), (double)materialShininess );
			ret += specularCoeff * materialSpecular * specular;
			
			return ret;
		}
	}
}

